package com.excesys.exsecs.access.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.excesys.exsecs.access.constant.RedisConstant;
import com.excesys.exsecs.access.constant.enums.ResponseEnum;
import com.excesys.exsecs.access.entity.*;
import com.excesys.exsecs.access.constant.enums.AccessThingModelEnum;
import com.excesys.exsecs.access.mapper.AccessCategoryMapper;
import com.excesys.exsecs.access.mapper.AccessDeviceInfoMapper;
import com.excesys.exsecs.access.mapper.AccessProductMapper;
import com.excesys.exsecs.access.mapper.AccessThingModelMapper;
import com.excesys.exsecs.access.service.AccessDeviceInfoService;
import com.excesys.exsecs.access.service.AccessSubareaService;
import com.excesys.exsecs.access.service.DataFlowOperationService;
import com.excesys.exsecs.access.vo.AccessDeviceInfoVO;
import com.excesys.exsecs.access.vo.ThingInvokeVO;
import com.excesys.exsecs.common.core.constant.SecurityConstants;
import com.excesys.exsecs.common.core.util.R;
import com.excesys.exsecs.common.excel.vo.ErrorMessage;
import com.excesys.exsecs.dto.InvokeDTO;
import com.excesys.exsecs.feign.RemoteMqttProvideService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 设备信息表
 *
 * @author lxc
 * @date 2024-05-06 17:44:39
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AccessDeviceInfoServiceImpl extends ServiceImpl<AccessDeviceInfoMapper, AccessDeviceInfo> implements AccessDeviceInfoService {

	private final AccessProductMapper accessProductMapper;
	private final AccessThingModelMapper accessThingModelMapper;
	private final DataFlowOperationService dataFlowOperationService;
	private final StringRedisTemplate redisTemplate;
	private final AccessSubareaService subareaService;
	private final RemoteMqttProvideService remoteMqttProvideService;

	@Override
	public IPage queryPage(Page page, AccessDeviceInfo accessDeviceInfo) {
		IPage<AccessDeviceInfo> ipage = this.baseMapper.queryPage(page, accessDeviceInfo);
		return ipage;
	}


	@Override
	public List<AccessDeviceInfo> queryList(LambdaQueryWrapper<AccessDeviceInfo> wrapper) {
		List<AccessDeviceInfo> list = this.list(wrapper);
		if (CollectionUtils.isNotEmpty(list)){
			list.forEach(it ->{
				AccessProduct product = accessProductMapper.selectOne(Wrappers.<AccessProduct>lambdaQuery().eq(AccessProduct::getProductKey, it.getProductKey()));
				it.setProductName(product.getName());
				it.setProductImg(product.getImg());
				if (ObjectUtils.isNotEmpty(it.getSubareaCode())){
					List<AccessSubarea> subareaList = subareaService.list(Wrappers.<AccessSubarea>lambdaQuery().eq(AccessSubarea::getSubareaCode, it.getSubareaCode()));
					if (CollectionUtils.isNotEmpty(subareaList)){
						it.setSubareaCn(subareaList.get(0).getSubareaCn());
					}
				}
			});
		}
		return list;
	}

	@Override
	public AccessDeviceInfo detail(String unitNo) {
		AccessDeviceInfo device = this.getOne(Wrappers.<AccessDeviceInfo>lambdaQuery().eq(AccessDeviceInfo::getUnitNo, unitNo));
		if (ObjectUtils.isEmpty(device)){
			return device;
		}
		AccessProduct product = accessProductMapper.selectOne(Wrappers.<AccessProduct>lambdaQuery().eq(AccessProduct::getProductKey, device.getProductKey()));
		device.setProductName(product.getName());
		device.setProductImg(product.getImg());
		if (ObjectUtils.isNotEmpty(device.getSubareaCode())){
			List<AccessSubarea> subareaList = subareaService.list(Wrappers.<AccessSubarea>lambdaQuery().eq(AccessSubarea::getSubareaCode, device.getSubareaCode()));
			if (CollectionUtils.isNotEmpty(subareaList)){
				device.setSubareaCn(subareaList.get(0).getSubareaCn());
			}
		}
		//获取物模型
		AccessThingModel thing = accessThingModelMapper.selectOne(Wrappers.<AccessThingModel>lambdaQuery().eq(AccessThingModel::getProductKey, product.getProductKey()));
		if (ObjectUtils.isEmpty(thing)){
			return device;
		}
		assembleThingModel(device, thing);
		return device;
	}

	@Override
	public boolean saveDevice(AccessDeviceInfo accessDeviceInfo) throws Exception {
		boolean save = this.save(accessDeviceInfo);
		//新增设备时，创建改设备的流水表
		String tableName = "t_" + accessDeviceInfo.getUnitNo();
		int count = dataFlowOperationService.checkTableExists("excesysiotx_link", tableName);
		if (count == 0){
			dataFlowOperationService.createCustomTable(tableName);
		}
		return save;
	}

	@Override
	//@Transactional(rollbackFor = Exception.class)
	public boolean removeBatch(ArrayList<Long> toList) {
		List<AccessDeviceInfo> devices = this.listByIds(toList);
		if (CollectionUtils.isNotEmpty(devices)){
			devices.forEach(it ->{
				String tableName = "t_" + it.getUnitNo();
				dataFlowOperationService.dropCustomTable(tableName);
			});
		}
		return this.removeBatchByIds(toList);
	}

	@Override
	public ThingInvokeVO invoke(ThingInvokeVO thingInvokeVO) throws Exception {
		ThingInvokeVO response = new ThingInvokeVO();
		String traceId = UUID.randomUUID().toString();
		thingInvokeVO.setTraceId(traceId);
		//查询该设备对应的接入类型
		AccessDeviceInfo device = this.getOne(Wrappers.<AccessDeviceInfo>lambdaQuery().eq(AccessDeviceInfo::getUnitNo, thingInvokeVO.getUnitNo()));
		AccessProduct product = accessProductMapper.selectOne(Wrappers.<AccessProduct>lambdaQuery().eq(AccessProduct::getProductKey, device.getProductKey()));
		//下发
		if ("MQTT".equals(product.getAccessWay())){
			String topic = "excesys/system/services/request/"+product.getProductKey();
			InvokeDTO dto = new InvokeDTO();
			dto.setTopic(topic);
			dto.setParams(JSONUtil.toJsonStr(thingInvokeVO));
			remoteMqttProvideService.addOwner(dto, SecurityConstants.FROM_IN);
			log.debug("发送Mqtt成功,topic: {},  data: {}", topic , thingInvokeVO.toString());
			for (int i = 0; i < 3; i++) {
				Thread.sleep(1000);
				Object redisObj = redisTemplate.opsForHash().get(RedisConstant.SERVICES_RESPONSE_LIST_KEY, traceId);
				if (ObjectUtils.isNotEmpty(redisObj)){
					return JSONUtil.toBean(JSONUtil.toJsonStr(redisObj), ThingInvokeVO.class);
				}
			}
		}
		return response;
	}

	@Override
	public R importDevice(List<AccessDeviceInfoVO> excelVOList, String productKey, BindingResult bindingResult) {
		// 个性化校验逻辑
		excelVOList = excelVOList.stream().filter(it -> ObjectUtils.isNotEmpty(it.getDeviceName())).collect(Collectors.toList());
		boolean repeat = excelVOList.stream().map(AccessDeviceInfoVO::getDeviceName).collect(Collectors.groupingBy(Function.identity())).values().stream().anyMatch(list1 -> list1.size() > 1);
		if (repeat) {
			return R.failed("存在重复设备导入,请仔细检查文件数据！");
		}
		// 执行数据插入操作 组装 UserDto
		for (AccessDeviceInfoVO excel : excelVOList) {
			AccessDeviceInfo info = new AccessDeviceInfo();
			info.setDeviceName(excel.getDeviceName());
			info.setProductKey(productKey);
			String unitNo = UUID.randomUUID().toString().replace("-", "");
			try {
				if (ObjectUtils.isNotEmpty(excel.getStaticProp())){
					JSONObject staticProp = JSONUtil.parseObj(excel.getStaticProp());
					info.setStaticProp(staticProp);
				}
				long count = this.count(Wrappers.<AccessDeviceInfo>lambdaQuery().eq(AccessDeviceInfo::getDeviceName, info.getDeviceName().trim()));
				if (count > 0){
					return R.failed("设备名称已存在,设备名称为：" + excel.getDeviceName());
				}
				info.setUnitNo(unitNo);
				this.saveDevice(info);
			} catch (Exception e){
				this.remove(Wrappers.<AccessDeviceInfo>lambdaQuery().eq(AccessDeviceInfo::getUnitNo, unitNo));
				return R.failed("设备添加失败");
			}
		}
		return R.ok("本次导入成功");
	}


	@Override
	public List<AccessDeviceInfoVO> exportDevice(AccessDeviceInfo accessDeviceInfo) {
		LambdaQueryWrapper<AccessDeviceInfo> wrapper = Wrappers.<AccessDeviceInfo>lambdaQuery()
				.eq(StrUtil.isNotBlank(accessDeviceInfo.getProductKey()), AccessDeviceInfo::getProductKey, accessDeviceInfo.getProductKey())
				.eq(ObjectUtils.isNotEmpty(accessDeviceInfo.getOnlineStatus()), AccessDeviceInfo::getOnlineStatus, accessDeviceInfo.getOnlineStatus())
				.eq(StrUtil.isNotBlank(accessDeviceInfo.getUnitNo()), AccessDeviceInfo::getUnitNo, accessDeviceInfo.getUnitNo())
				.like(StrUtil.isNotEmpty(accessDeviceInfo.getDeviceName()), AccessDeviceInfo::getDeviceName, accessDeviceInfo.getDeviceName());
		List<AccessDeviceInfo> list = this.list(wrapper);
		if (CollectionUtils.isNotEmpty(list)){
			list.forEach(it ->{
				AccessProduct product = accessProductMapper.selectOne(Wrappers.<AccessProduct>lambdaQuery().eq(AccessProduct::getProductKey, it.getProductKey()));
				it.setProductName(product.getName());
			});
		}
		List<AccessDeviceInfoVO> voList = list.stream().map(deivce -> {
			AccessDeviceInfoVO excelVO = new AccessDeviceInfoVO();
			BeanUtils.copyProperties(deivce, excelVO);
			if (ObjectUtils.isNotEmpty(deivce.getStaticProp())){
				excelVO.setStaticProp(JSONUtil.toJsonStr(deivce.getStaticProp()));
			}
			excelVO.setOnlineStatus(deivce.getOnlineStatus().toString());
			return excelVO;
		}).collect(Collectors.toList());
		return voList;
	}

	@Override
	public boolean setStaticProp(AccessDeviceInfo accessDeviceInfo) {
		AccessDeviceInfo device = this.baseMapper.selectOne(Wrappers.<AccessDeviceInfo>lambdaQuery()
				.eq(AccessDeviceInfo::getUnitNo, accessDeviceInfo.getUnitNo()));
		if (ObjectUtils.isEmpty(device)){
			return false;
		}
		JSONObject staticProp = device.getStaticProp();
		if (ObjectUtils.isNotEmpty(staticProp)){
			staticProp.putAll(accessDeviceInfo.getStaticProp());
		} else{
			staticProp = accessDeviceInfo.getStaticProp();
		}
		//更新设备表中的静态变量json
		boolean update = this.update(Wrappers.<AccessDeviceInfo>lambdaUpdate()
				.set(AccessDeviceInfo::getStaticProp, staticProp.toString())
				.eq(AccessDeviceInfo::getUnitNo, device.getUnitNo()));
		return update;
	}

	private AccessDeviceInfo assembleThingModel (AccessDeviceInfo device, AccessThingModel thing){
		if (ObjectUtils.isEmpty(thing.getModel())){
			return device;
		}
		JSONObject model = thing.getModel();
		List<JSONObject> properties = model.getBeanList(AccessThingModelEnum.PROPERTIES.getCode(), JSONObject.class);
		//查询redis中的属性实时数据
		Object paload = redisTemplate.opsForHash().get(RedisConstant.PROPERTIES_LIST_KEY, device.getUnitNo());
		JSONObject staticProp = device.getStaticProp();
		if (ObjectUtils.isNotEmpty(paload)){
			properties = JSONUtil.toList(JSONUtil.parseArray(paload), JSONObject.class);
			if (ObjectUtils.isNotEmpty(staticProp)) {
				properties.forEach(item -> {
					Object value = staticProp.get(item.getStr(RedisConstant.IDENTIFIER));
					if (ObjectUtils.isNotEmpty(value)) {
						item.set(RedisConstant.VALUE, value);
					}
				});
			}
		} else {
			if (ObjectUtils.isNotEmpty(staticProp)) {
				properties.forEach(item -> {
					Object value = staticProp.get(item.getStr(RedisConstant.IDENTIFIER));
					if (ObjectUtils.isNotEmpty(value)) {
						item.set(RedisConstant.VALUE, value);
					}
				});
			}
		}
		device.setProperties(properties);
		device.setServices(model.getBeanList(AccessThingModelEnum.SERVICES.getCode(), JSONObject.class));
		device.setEvents(model.getBeanList(AccessThingModelEnum.EVENTS.getCode(), JSONObject.class));;
		return device;
	}
}